package ner;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import jclp.JCLP;


public class POStagger {
	
	public static final String UNKNOWN_TAG="UNKNOWN";
	private static final char BIGLETTER_ASCIICODE = 65;
	private static final PartOfSpeech[] POSArray = {};
//original from pdf, incorrect	
/*
	private final static Set<Integer> MASCULINE_VERB=new HashSet<Integer>(Arrays.asList(new Integer[]{11,12,13,26,27,28}));
	private final static Set<Integer> FEMININE_VERB=new HashSet<Integer>(Arrays.asList(new Integer[]{14,15,16,29,30,31}));
	private final static Set<Integer> NEUTER_VERB=new HashSet<Integer>(Arrays.asList(new Integer[]{17,18,19,32,33,34}));
	private final static Set<Integer> MASCULINE_PLURAL_VERB=new HashSet<Integer>(Arrays.asList(new Integer[]{20,21,22,35,36,37}));
	private final static Set<Integer> NONMASCULINE_PLURAL_VERB=new HashSet<Integer>(Arrays.asList(new Integer[]{23,24,25,38,39,40}));
*/
//modfied
	private final static Set<Integer> MASCULINE_VERB=new HashSet<Integer>(Arrays.asList(new Integer[]{15,16,17,30,31,32}));
	private final static Set<Integer> FEMININE_VERB=new HashSet<Integer>(Arrays.asList(new Integer[]{18,19,20,33,34,35}));
	private final static Set<Integer> NEUTER_VERB=new HashSet<Integer>(Arrays.asList(new Integer[]{21,22,23,36,37,38}));
	private final static Set<Integer> MASCULINE_PLURAL_VERB=new HashSet<Integer>(Arrays.asList(new Integer[]{24,25,26,39,40,41}));
	private final static Set<Integer> NONMASCULINE_PLURAL_VERB=new HashSet<Integer>(Arrays.asList(new Integer[]{27,28,29,42,43,44}));
	
	private final static Set<Integer> SINGULAR_VERB=initializeSingularVerb();
	private final static Set<Integer> PLURAL_VERB=initializePluralVerb();
	
	
	static{
		System.out.println(POStagger.class.getName()+": Loading clp dictionary");
		JCLP.init();
		System.out.println(POStagger.class.getName()+":clp dictionary loaded");
	}

	private static Set<Integer> initializeSingularVerb() {
		
		Set<Integer> result=new HashSet<Integer>();
		result.addAll(MASCULINE_VERB);
		result.addAll(FEMININE_VERB);
		result.addAll(NEUTER_VERB);
		result.addAll(Arrays.asList(new Integer[]{2,3,4,8}));
		return result;
		
	}

	private static Set<Integer> initializePluralVerb() {
		
		Set<Integer> result=new HashSet<Integer>();
		result.addAll(MASCULINE_PLURAL_VERB);
		result.addAll(NONMASCULINE_PLURAL_VERB);
		result.addAll(Arrays.asList(new Integer[]{5,6,7,10,11}));
		return result;
		
	}

	//enum?
	public TaggingResult recognizePOS(List<String> words) throws UnsupportedEncodingException{
		
		
		List<String> POStags=new ArrayList<String>();
		List<String> baseWords=new ArrayList<String>();
		
		for(String aWord:words){
			
			//System.out.println("word1");
			
			int[] ids =JCLP.rec(aWord);
			//System.out.println("word2");
		
			if (ids.length == 0) {
				//POStags.add(UNKNOWN_TAG);
				baseWords.add(UNKNOWN_TAG);
			} else {	//System.out.println("word3");
			//	System.out.println("INVOKING CLP LABEL");
			//	POStags.add(String.valueOf(JCLP.label(ids[0]).charAt(0)));
				//System.out.println("word4");
				String baseForm=JCLP.bform(ids[0]);
	
				baseWords.add(baseForm.toLowerCase().trim());
				//System.out.println("word5");
				//System.out.println(baseForm);
			}
		}

		return new TaggingResult(POStags,baseWords);
		
	}
	
	
	public static PartOfSpeech getPOS(String word){
		
		int[] ids =JCLP.rec(word);
		if(ids==null || ids.length==0)
			return null;
		
		String label=JCLP.label(ids[0]);
		if(label==null)
			return null;
		return PartOfSpeech.getPOSById(label.charAt(0)-BIGLETTER_ASCIICODE);
		
	}
	
	public static List<Case> getPossibleCasesOfAdjective(String word,GrammaticalNumber gnumberFilter,GrammaticalGender genderFilter){
		
		int[] orderNumbersInForms = getOrderNumbersInFormsVec(word);
		if(orderNumbersInForms==null || orderNumbersInForms.length==0)
			return null;
		List<Case> result=new ArrayList<Case>();
		for(int orderNumber:orderNumbersInForms){
		
			if((gnumberFilter.equals(GrammaticalNumber.SINGULAR) && orderNumber<29) ||
			   (gnumberFilter.equals(GrammaticalNumber.PLURAL) && orderNumber>=29)){
				GrammaticalGender gg=orderNumberToGender(orderNumber,PartOfSpeech.ADJECTIVE);
				Case caseOfForm=Case.getById(orderNumber);
				
				if(caseOfForm!=null && gg!=null && (gnumberFilter.equals(GrammaticalNumber.PLURAL) || gg.equals(genderFilter))){
					result.add(caseOfForm);
				}
			}
			
		}
		
		return result;
		
	}
	public static List<Case> getPossibleCasesOfNoun(String word,
			GrammaticalNumber gnumberFilter) {
		
		int[] orderNumbersInForms = getOrderNumbersInFormsVec(word);
		if(orderNumbersInForms==null || orderNumbersInForms.length==0)
			return null;
		List<Case> result=new ArrayList<Case>();
		for(int orderNumber:orderNumbersInForms){
		
			if((gnumberFilter.equals(GrammaticalNumber.SINGULAR) && orderNumber<=7) ||
			   (gnumberFilter.equals(GrammaticalNumber.PLURAL) && orderNumber>7)){
				Case caseOfForm=Case.getById(orderNumber);	
				if(caseOfForm!=null)
					result.add(caseOfForm);
				
			}
			
		}
		
		return result;
		
	}
	
	private static int[] getOrderNumbersInFormsVec(String word) {
		int[] ids =JCLP.rec(word);
		if(ids==null || ids.length==0)
			return null;
		else{
			int[] orderNumbersInForms=JCLP.vec(ids[0],word);
			return orderNumbersInForms;
		}
	}
	

	
	
	public static Pair<GrammaticalGender,GrammaticalNumber> getGrammaticalGenderAndNumber(String word){
	
		PartOfSpeech pos=getPOS(word);
		if(pos==null)
			return new Pair(null,null);
		if(pos.equals(PartOfSpeech.ADJECTIVE)||pos.equals(PartOfSpeech.VERB) || pos.equals(PartOfSpeech.NOUN)){
			
			int[] orderNumbersInForms = getOrderNumbersInFormsVec(word);
			Map<GrammaticalGender,Integer> ggOccurrences=new HashMap<GrammaticalGender, Integer>();
			
			int indicationForSingular=0;
			int indicationForPlural=0;
			
			for(int number:orderNumbersInForms){
				GrammaticalGender gg=orderNumberToGender(number,pos);
				Integer value;
				if((value=ggOccurrences.get(gg))!=null)
					ggOccurrences.put(gg, value+1);
				else
					ggOccurrences.put(gg, 1);
					
				GrammaticalNumber gn=orderNumberToGrammaticalNumber(number,pos);
				if(gn!=null){
					if(gn.equals(GrammaticalNumber.SINGULAR))
						indicationForSingular++;
					else if(gn.equals(GrammaticalNumber.PLURAL))
						indicationForPlural++;
				}
			}
			GrammaticalNumber chosenGrammaticalNumber=null;		
			if(indicationForPlural>indicationForSingular)
				chosenGrammaticalNumber=GrammaticalNumber.PLURAL;	
			else
				chosenGrammaticalNumber=GrammaticalNumber.SINGULAR;
			
			GrammaticalGender chosenGrammaticalGender=Util.getMostFrequentFromMap(ggOccurrences);
			
			
			
			return new Pair<GrammaticalGender,GrammaticalNumber>(chosenGrammaticalGender,chosenGrammaticalNumber);
			
		}
		else
			return new Pair(null,null);
		
		
	}
		
	
	private static GrammaticalNumber orderNumberToGrammaticalNumber(int number,
			PartOfSpeech pos) {
	
		if(pos.equals(PartOfSpeech.ADJECTIVE)){
			int id=(number-1)/7;
			if(id<=3)
				return GrammaticalNumber.SINGULAR;
			else
				return GrammaticalNumber.PLURAL;
		}
		else if(pos.equals(PartOfSpeech.VERB)){
			
			if(SINGULAR_VERB.contains(number))
				return GrammaticalNumber.SINGULAR;
			else if(PLURAL_VERB.contains(number))
				return GrammaticalNumber.PLURAL;
			else
				return null;
			
		}
		else if(pos.equals(PartOfSpeech.NOUN)){
			
			if(number<=7)
				return GrammaticalNumber.SINGULAR;
			else if(number>7)
				return GrammaticalNumber.PLURAL;
			else
				return null;
			
		}
		return null;
	}

	private static GrammaticalGender orderNumberToGender(int number,PartOfSpeech pos) {
		
		if(pos.equals(PartOfSpeech.ADJECTIVE)){
			int id=(number-1)/7;
			switch(id){
				case 0:
					return GrammaticalGender.MASCULINE;
				case 1:
					return GrammaticalGender.MASCULINE;
				case 2:
					return GrammaticalGender.FEMININE;
				case 3:
					return GrammaticalGender.NEUTER;
				case 4:
					return GrammaticalGender.MASCULINE_PLURAL;
				case 5:
					return GrammaticalGender.NONMASCULINE_PLURAL;
			}
		
			
		}
		else if(pos.equals(PartOfSpeech.VERB)){
			if(MASCULINE_VERB.contains(number))
				return GrammaticalGender.MASCULINE;
			else if(FEMININE_VERB.contains(number))
				return GrammaticalGender.FEMININE;
			else if(NEUTER_VERB.contains(number)){
				return GrammaticalGender.NEUTER;
			}
			else if(MASCULINE_PLURAL_VERB.contains(number)){
				return GrammaticalGender.MASCULINE_PLURAL;
			}
			else if(NONMASCULINE_PLURAL_VERB.contains(number)){
				return GrammaticalGender.NONMASCULINE_PLURAL;
			}
		}
		return null;
		
	}

	
	
	
	public static class TaggingResult{
		
		private List<String> POStags;
		private List<String> baseWords;
	
		public TaggingResult(List<String> POStags,List<String> baseWords){
			
			this.POStags = POStags;
			this.baseWords = baseWords;
			
		}
		
		public List<String> getPOStags() {
			return POStags;
		}

		public List<String> getBaseWords() {
			return baseWords;
		}

	}






	

}

	